package com.fezs.security.filter;

import com.fezs.common.model.LoginUser;
import com.fezs.common.model.response.ResultBody;
import com.fezs.security.config.properties.WebSecurityProperties;
import com.fezs.security.utils.SecurityUtils;
import com.fezs.web.mvc.handle.GlobalExceptionHandler;
import com.fezs.web.mvc.utils.WebServletUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.Duration;

/**
 * Token 过滤器，验证 token 的有效性
 * 验证通过后，获得 {@link LoginUser} 信息，并加入到上下文
 */
@RequiredArgsConstructor
public class TokenAuthenticationFilter extends OncePerRequestFilter {

    private final WebSecurityProperties webSecurityProperties;

    private final GlobalExceptionHandler globalExceptionHandler;

    @Override
    @SuppressWarnings("NullableProblems")
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        try {
            // 基于 Token 获得用户
            LoginUser loginUser = SecurityUtils.getLoginUserFromToken(request, webSecurityProperties.getTokenHeader());

            // 设置当前用户
            if (loginUser != null && this.checkLoginUserExpiry(loginUser)) {
                // 往SecurityContext注入登录对象, 后续会在FilterSecurityInterceptor中以此鉴权, 更具体的验证在WebExpressionVoter中
                SecurityUtils.setLoginUserAuth(loginUser, request);
            }
        } catch (Throwable ex) {
            ResultBody<?> result = globalExceptionHandler.allExceptionHandler(request, ex);
            WebServletUtils.writeJSON(response, result);
            return;
        }

        // 继续过滤链
        chain.doFilter(request, response);
    }

    /**
     * 根据RefreshIntervalTime刷新登录用户的过期时间
     */
    private boolean checkLoginUserExpiry(LoginUser loginUser) {
        long loginTime = loginUser.getLoginTime();
        long expireTime = loginUser.getExpireTime();
        long currentTime = System.currentTimeMillis();
        // 过了有效期, 登录失效
        if (currentTime > expireTime) {
            return false;
        }
        Integer timeout = webSecurityProperties.getTimeout();
        Integer refreshIntervalTime = webSecurityProperties.getRefreshIntervalTime();
        if (loginTime + refreshIntervalTime < currentTime) {
            loginUser.setLoginTime(currentTime);
            loginUser.setExpireTime(currentTime + Duration.ofSeconds(timeout).toMillis());
            SecurityUtils.setLoginUserByCache(loginUser, Duration.ofSeconds(timeout));
        }
        return true;
    }

}
